//
//  main.swift
//  APIParserTool
//
//  Created by FH on 2021/1/7.
//  Copyright © 2021 Agora. All rights reserved.
//

import Foundation

func runScript() {
    // create context
    let ctxEnvVars = ["TERM_PROGRAM", "SHELL", "TERM", "TMPDIR", "Apple_PubSub_Socket_Render", "TERM_PROGRAM_VERSION",
                      "TERM_SESSION_ID", "USER", "SSH_AUTH_SOCK", "__CF_USER_TEXT_ENCODING", "XPC_FLAGS", "XPC_SERVICE_NAME",
                      "SHLVL", "HOME", "LOGNAME", "LC_CTYPE", "_"]
    var ctx = CustomContext(main)
    ctx.env = ctx.env.filterByKeys(keys: ctxEnvVars)
    ctx.env["PATH"] = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    // arg names
    let sdkVersionLabel = "RTC_SDK_VERSION="
    let sdkDownloadURLLabel = "RTC_SDK_URL="
    let branchVersionLabel = "APP_BRANCH="
    let unzipFolderPathLabel = "unzip_folder_path="
    
    #if DEBUG
        CommandLine.arguments.append("\(sdkVersionLabel)carbon")
//        CommandLine.arguments.append("\(sdkDownloadURLLabel)https://accs-bot:accsbot123@artifactory-api.bj2.agoralab.co/artifactory/ACCS_repo/vcarbon/AgoraSDK/iOS/Agora_Native_SDK_for_iOS_vcarbon_FULL_20210312_4146_59105.zip")
        CommandLine.arguments.append("\(sdkDownloadURLLabel)http://192.168.99.149:8086/v3.3.0/AgoraSDK/Mac/Agora_Native_SDK_for_Mac_v3.3.0_FULL_20210111_2522_39272.zip")
        CommandLine.arguments.append("\(branchVersionLabel)release/3.0.0")
//        CommandLine.arguments.append("\(unzipFolderPathLabel)/Users/fh/Desktop/Agora_Native_SDK_for_iOS_FULL")
        ctx.currentdirectory = "/Users/fh/Desktop/workspaces-3.1.0/hermes-mac"
    #endif
    
    let fileMgr = FileManager.default
    // check command tools
    let findUnzip = ctx.run("which", "unzip")
    if findUnzip.stdout.count == 0 {
        print("[ERROR] not found command `unzip`")
        return
    }
    let findWget = ctx.run("which", "wget")
    if findWget.stdout.count == 0 {
        print("[ERROR] not found command `wget`")
        return
    }
    // check command line args and set core states
    var sdkVersion = "3.0.0"
    var sdkDownloadURL = ""
    var sdkName = ""
    var isLessVer3 = false
    var unzipFolderPath = ""
    let args = CommandLine.safeArguments
    if args.count < 4 {
        print("Please provide the following arguments:")
        print("\(sdkVersionLabel)3.0.x")
        print("\(branchVersionLabel)release/3.0.0 (2.9.0)")
        print("\(sdkDownloadURLLabel)http://192.168.99.149:8086/v3.3.0/XXX or \(unzipFolderPathLabel)/Users/XXX/Agora_Native_SDK_for_Mac_FULL")
        return
    } else {
        var sdkDownloadURLArg = ""
        for i in 1..<args.count {
            let arg = args[i]
            if arg.hasPrefix(sdkVersionLabel) {
                sdkVersion = String(arg.suffix(arg.count - sdkVersionLabel.count))
            } else if arg.hasPrefix(sdkDownloadURLLabel) {
                sdkDownloadURL = String(arg.suffix(arg.count - sdkDownloadURLLabel.count))
                if let url = URL(string: sdkDownloadURL) {
                    sdkName = url.lastPathComponent
                } else {
                    sdkDownloadURLArg = arg
                }
            } else if arg.hasPrefix(branchVersionLabel) {
                let branchVersion = String(arg.suffix(arg.count - branchVersionLabel.count))
                if branchVersion == "release/2.9.0" {
                    isLessVer3 = true
                } else if branchVersion == "release/3.0.0" {
                    isLessVer3 = false
                }
            } else if arg.hasPrefix(unzipFolderPathLabel) {
                let path = String(arg.suffix(arg.count - unzipFolderPathLabel.count))
                if path.count > 0 && fileMgr.fileExists(atPath: path) {
                    unzipFolderPath = path
                }
            }
        }
        if sdkName == "" && unzipFolderPath == "" {
            if sdkDownloadURLArg == "" {
                print("[ERROR] \(sdkDownloadURLLabel) is empty.")
            } else {
                print("[ERROR] \(sdkDownloadURLLabel)\(sdkDownloadURLArg) is invalid.")
            }
            return
        }
    }
    // check dependency folders
    let frameworksFolder = DirectoryPath(base: ctx.currentdirectory, relative: "frameworks")
    if !fileMgr.fileExists(atPath: frameworksFolder.absoluteString) {
        try! fileMgr.createDirectory(atPath: frameworksFolder.absoluteString, withIntermediateDirectories: true, attributes: nil)
    }
    let tmpPath = DirectoryPath(base: ctx.currentdirectory, relative: "tmp")
    if !fileMgr.fileExists(atPath: tmpPath.absoluteString) {
        try! fileMgr.createDirectory(atPath: tmpPath.absoluteString, withIntermediateDirectories: true, attributes: nil)
    }
    // download sdk zip file
    if unzipFolderPath == "" {
        if fileMgr.fileExists(atPath: tmpPath.append(file: sdkName).absoluteString) {
            #if !DEBUG
            try! fileMgr.removeItem(atPath: tmpPath.append(file: sdkName).absoluteString)
            #endif
        }
        if !fileMgr.fileExists(atPath: tmpPath.append(file: sdkName).absoluteString) {
            try! ctx.runAndPrint("wget", sdkDownloadURL, "-O", tmpPath.append(file: sdkName).absoluteString)
            if !fileMgr.fileExists(atPath: tmpPath.append(file: sdkName).absoluteString) {
                print("[ERROR] not found \(tmpPath.append(file: sdkName).absoluteString)")
                return
            }
        }
    }
    // unzip sdk files
    var isMacPlatform = true
    if sdkName.contains("_iOS_") || unzipFolderPath.contains("_iOS_") {
        isMacPlatform = false
    }
    let unzipSdkFullFolderName = "Agora_Native_SDK_for_\(isMacPlatform ? "Mac" : "iOS")_FULL"
    let unzipSdkVoiceFolderName = "Agora_Native_SDK_for_\(isMacPlatform ? "Mac" : "iOS")_VOICE"
    if unzipFolderPath.count > 0
        && !(unzipFolderPath.contains(unzipSdkFullFolderName) || unzipFolderPath.contains(unzipSdkVoiceFolderName)) {
        print("[ERROR] \(unzipFolderPathLabel)\(unzipFolderPath) is invalid.")
        return
    }
    let isVoiceSDK = sdkDownloadURL.contains("_VOICE")
    if unzipFolderPath == "" {
        try! ctx.runAndPrint("rm", "-rf", tmpPath.append(directory: unzipSdkFullFolderName).absoluteString)
        try! ctx.runAndPrint("rm", "-rf", tmpPath.append(directory: unzipSdkVoiceFolderName).absoluteString)
        try! ctx.runAndPrint("unzip", tmpPath.append(file: sdkName).absoluteString, "-d", tmpPath.absoluteString)
    }
    // dump clang ast nodes
    let dumpFolder = tmpPath.append(directory: "dump")
    if fileMgr.fileExists(atPath: dumpFolder.absoluteString) {
        try! fileMgr.removeItem(atPath: dumpFolder.absoluteString)
    }
    try! fileMgr.createDirectory(atPath: dumpFolder.absoluteString, withIntermediateDirectories: true, attributes: nil)
    var unzipSdkFolder = tmpPath.append(directory: isVoiceSDK ? unzipSdkVoiceFolderName : unzipSdkFullFolderName)
    if unzipFolderPath.count > 0 {
        unzipSdkFolder = DirectoryPath(unzipFolderPath)
    }
    if !fileMgr.fileExists(atPath: unzipSdkFolder.absoluteString) {
        print("[ERROR] not found unzip folder path: \(unzipSdkFolder.absoluteString)")
        return
    }
    unzipSdkFolder = unzipSdkFolder.append(directory: "libs")
    var agoraSDKName = "AgoraRtcKit.framework"
    if isLessVer3 {
        agoraSDKName = isVoiceSDK ? "AgoraAudioKit.framework" : "AgoraRtcEngineKit.framework"
    } else if !fileMgr.fileExists(atPath: unzipSdkFolder.append(directory: agoraSDKName).absoluteString) {
        agoraSDKName = "AgoraRtcKit.xcframework/ios-armv7_arm64/AgoraRtcKit.framework"
    }
    let unzipSdkHeadersFolder = unzipSdkFolder.append(directory: agoraSDKName).append(directory: "Headers")
    let sdkFolderPath = frameworksFolder.append(directory: "SDKs").append(directory: sdkVersion)
                        .append(directory: isVoiceSDK ? "voice" : "full").absoluteString
    if fileMgr.fileExists(atPath: sdkFolderPath) {
        try! fileMgr.removeItem(atPath: sdkFolderPath)
    }
    try! fileMgr.createDirectory(atPath: sdkFolderPath, withIntermediateDirectories: true, attributes: nil)
    try? ctx.runAndPrint("cp", "-RP", unzipSdkFolder.absoluteString+"/", sdkFolderPath)
    try! ctx.runAndPrint("cp", "-r", unzipSdkHeadersFolder.absoluteString, dumpFolder.absoluteString)
    try! ctx.runAndPrint("rm", dumpFolder.append(directory: "Headers").append(file: "AgoraRtcEngineKit.h").absoluteString)
    
    let agoraKitHeaderReader = LineReader(path: unzipSdkHeadersFolder.append(file: "AgoraRtcEngineKit.h").absoluteString)!
    var updateHeader = [String]()
    for line in agoraKitHeaderReader {
        if line.contains("#if") || line.contains("#endif") {
            continue
        } else {
            updateHeader.append(line)
        }
    }
    let dumpHeadersFolder = dumpFolder.append(directory: "Headers")
    try! updateHeader.joined().write(toFile: dumpHeadersFolder.append(file: "AgoraRtcEngineKit.h").absoluteString,
                                     atomically: true, encoding: .utf8)
    let dumpFileName = "\(sdkVersion)_apis.dump"
    let dumpFilePath = dumpFolder.append(file: dumpFileName).absoluteString
    if fileMgr.fileExists(atPath: dumpFilePath) {
        try! fileMgr.removeItem(atPath: dumpFilePath)
    }
    var dumpAPIFile = try! open(forWriting: dumpFilePath)
    print(">>> clang -ast-dump and parse nodes, please wait some seconds.")
    ctx.run("clang", "-Xclang", "-ast-dump", "-x", "objective-c", "-fsyntax-only",
            "-fno-color-diagnostics", "-fno-show-column", "-fno-show-source-location",
            dumpHeadersFolder.append(file: "AgoraRtcChannel.h").absoluteString,
            dumpHeadersFolder.append(file: "AgoraRtcEngineKit.h").absoluteString)
        .stdout.write(to: &dumpAPIFile)
    // parse apis dump file
    let parser = APIsParser(keyWord: "Agora", dumpFilePath: dumpFilePath)
    var nodes = parser.parse()
    nodes = APIsTagsHandler(astNodes: nodes,
                            objcAPIsHeaderPath: dumpHeadersFolder.append(file: "AgoraRtcEngineKit.h").absoluteString,
                            apiClassName: "AgoraRtcEngineKit",
                            delegateClassName: "AgoraRtcEngineDelegate")
        .parse()
    // save json apis to file
    let dumpRootFolder = frameworksFolder.append(directory: "Dumps")
    let dumpJsonVerFolder = dumpRootFolder.append(directory: sdkVersion)
    if !fileMgr.fileExists(atPath: dumpJsonVerFolder.absoluteString) {
        try! fileMgr.createDirectory(atPath: dumpJsonVerFolder.absoluteString, withIntermediateDirectories: true, attributes: nil)
    }
    let jsonData = try! JSONEncoder().encode(nodes)
    let jsonStr = String(data: jsonData, encoding: .utf8)!
    try! jsonStr.write(toFile: dumpJsonVerFolder.append(file: "\(sdkVersion)_\(isVoiceSDK ? "voice" : "full")_apis.json").absoluteString, atomically: true, encoding: .utf8)
    try! ctx.runAndPrint("cp", dumpHeadersFolder.append(file: "AgoraRtcEngineKit.h").absoluteString, dumpJsonVerFolder.append(file: "\(sdkVersion)_AgoraRtcEngineKit.header").absoluteString)
    // for project link and compile
    try! jsonStr.write(toFile: dumpRootFolder.append(file: "apis.json").absoluteString, atomically: true, encoding: .utf8)
    try! ctx.runAndPrint("cp", dumpHeadersFolder.append(file: "AgoraRtcEngineKit.h").absoluteString,
                         dumpRootFolder.append(file: "AgoraRtcEngineKit.header").absoluteString)
    
    // TODO: check 命令行运行权限
    print(">>> parse complete.")
}

runScript()
